package com.cloud.schedule.api.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.transaction.Transactional;

import org.quartz.CronScheduleBuilder;
import org.quartz.CronTrigger;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.cloud.schedule.api.job.QuartzJobFactory;
import com.cloud.schedule.api.job.ScheduleJob;
import com.cloud.schedule.api.service.ScheduleService;

@Service
@Transactional
public class ScheduleServiceImpl implements ScheduleService {

	@Autowired
	private Scheduler scheduler;

	@Override
	public List<ScheduleJob> listJobsByGroupName(String groupName) {
		List<ScheduleJob> jobList = new ArrayList<>();
		try {
			GroupMatcher<JobKey> matcher = GroupMatcher.jobGroupEquals(groupName);
			Set<JobKey> jobKeySet = scheduler.getJobKeys(matcher);
			for (JobKey jobKey : jobKeySet) {
				List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
				for (Trigger trigger : triggers) {
					ScheduleJob scheduleJob = new ScheduleJob();
					this.wrapScheduleJob(scheduleJob, scheduler, jobKey, trigger);
					jobList.add(scheduleJob);
				}
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return jobList;
	}

	private void wrapScheduleJob(ScheduleJob scheduleJob, Scheduler scheduler2, JobKey jobKey, Trigger trigger) {
		try {
			scheduleJob.setJobName(jobKey.getName());
			scheduleJob.setJobGroup(jobKey.getGroup());

			JobDetail jobDetail = scheduler.getJobDetail(jobKey);
			ScheduleJob job = (ScheduleJob) jobDetail.getJobDataMap().get("scheduleJob");
			scheduleJob.setDesc(job.getDesc());
			scheduleJob.setInterfaceName(job.getInterfaceName());

			Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
			scheduleJob.setJobStatus(triggerState.name());
			if (trigger instanceof CronTrigger) {
				CronTrigger cronTrigger = (CronTrigger) trigger;
				String cronExpression = cronTrigger.getCronExpression();
				scheduleJob.setCronExpression(cronExpression);
			}
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void pauseJob(ScheduleJob job) {
		JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
		try {
			scheduler.pauseJob(jobKey);
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void resumeJob(ScheduleJob job) {
		JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
		try {
			scheduler.resumeJob(jobKey);
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void deleteJob(ScheduleJob job) {
		JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
		try {
			scheduler.deleteJob(jobKey);
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void runOnce(ScheduleJob job) {
		JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
		try {
			scheduler.deleteJob(jobKey);
		} catch (SchedulerException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void saveUpdate(ScheduleJob job) {
		if (StringUtils.isEmpty(job.getJobId())) {
			TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
			CronTrigger trigger = null;
			try {
				trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
				if (trigger != null) {
					throw new RuntimeException("job already exists!");
				}

				JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class)
						.withIdentity(job.getJobName(), job.getJobGroup()).build();
				jobDetail.getJobDataMap().put("scheduleJob", job);

				CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
				trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup())
						.withSchedule(cronScheduleBuilder).build();

				scheduler.scheduleJob(jobDetail, trigger);
			} catch (SchedulerException e) {
				throw new RuntimeException(e);
			}
		} else {
			try {
				TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());
				CronTrigger cronTrigger = (CronTrigger) scheduler.getTrigger(triggerKey);
				CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
				cronTrigger = cronTrigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(cronScheduleBuilder)
						.build();
				scheduler.rescheduleJob(triggerKey, cronTrigger);
			} catch (SchedulerException e) {
				throw new RuntimeException(e);
			}
		}
	}

	@Override
	public ScheduleJob showJobDetail(ScheduleJob job) {
		if (StringUtils.isEmpty(job.getJobName())) {
			return job;
		} else {
			try {
				JobKey jobKey = JobKey.jobKey(job.getJobName(), job.getJobGroup());
				List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
				for (Trigger trigger : triggers) {
					ScheduleJob scheduleJob = new ScheduleJob();
					this.wrapScheduleJob(scheduleJob, scheduler, jobKey, trigger);
					return scheduleJob;
				}
			} catch (SchedulerException e) {
				throw new RuntimeException(e);
			}
		}
		return null;
	}

}
